﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.IO;
using System.Diagnostics;
using RoccaStore.Services;
using RoccaStore.Web.Models;
using System.Xml.Linq;
using System.Net;
using System.Threading.Tasks;
using System.Threading;
using System.Runtime.Serialization.Formatters.Binary;
using RoccaStore.Upload;
using RoccaStore.Mailers;
using Mvc.Mailer;
using RoccaStore.Utils;
using RoccaStore.Web.Filters;
using Facebook;
using System.Configuration;
using RoccaStore.Extensions;

namespace RoccaStore.Web.Controllers
{
    [AccountFilter]
    public class FileController : Controller
    {
        private readonly IFileSendService _fileSendService;
        private readonly IFileInfoService _fileInfoService;
        private readonly IUserMailer _userMailer;
        private readonly ISlugService _slugService;
        private readonly IAccountService _accountService;

        public FileController(
            IFileSendService fileSendService,
            IFileInfoService fileInfoService,
            IUserMailer userMailer,
            ISlugService slugService,
            IAccountService accountService)
        {
            _fileSendService = fileSendService;
            _fileInfoService = fileInfoService;
            _userMailer = userMailer;
            _slugService = slugService;
            _accountService = accountService;
        }

        [HttpPost]
        public JsonResult Upload()
        {
            try
            {
                foreach (string file in Request.Files)
                {
                    HttpPostedFileBase fileBase = Request.Files[file];

                    if (fileBase.ContentLength == 0)
                        continue;

                    // create and save file
                    FileInfo fileInfo = _fileInfoService.SaveAs(fileBase);

                    // create slug data                   
                    Slug slug = _slugService.CreateSlug(fileInfo.Filename, fileInfo.ID);

                    return Json(new { success = true, slug = slug.Name, hash = slug.Key, filename = fileInfo.Filename }, JsonRequestBehavior.AllowGet);
                }
            }
            catch (Exception ex)
            {
                return Json(new { success = false, message = ex.Message });
            }

            return Json(new { success = false, message = "No files uploaded" });
        }

        [HttpPost]
        public ActionResult ConfirmQueue(string qSlugs, string qHashes, string qName, bool? compress)
        {
            string[] arySlugs = Newtonsoft.Json.JsonConvert.DeserializeObject<string[]>(qSlugs);
            string[] aryHashes = Newtonsoft.Json.JsonConvert.DeserializeObject<string[]>(qHashes);

            Slug slug = _slugService.CreateSlug(qName, null);
            slug.Type = Enums.SlugType.Group;
            slug.Title = qName;

            for (int i = 0; i < arySlugs.Length; i++)
            {
                Slug childSlug = _slugService.GetByName(arySlugs[i]);
                childSlug.ParentID = slug.ID;
                _slugService.Save(childSlug);
            }

            if (compress.HasValue && compress.Value)
            {
                // compress the files to download them all at once
                int fileId = _fileInfoService.Compress(slug.ID);
                slug.FileID = fileId;
            }

            _slugService.Save(slug);

            return RedirectToAction("complete", "file", new { slug = slug.Name, hash = slug.Key });
        }

        [HttpGet]
        public ActionResult Secure(string slug, string hash)
        {
            Slug fileSlug = _slugService.GetByName(slug);

            if (fileSlug.Deleted.HasValue)
                return RedirectToAction("deleted", "errors");

            if (fileSlug.Key.Equals(hash))
            {
                fileSlug.IsSecure = true;
                _slugService.Save(fileSlug);

                foreach (var child in fileSlug.Files)
                {
                    child.IsSecure = true;
                    _slugService.Save(child);
                }

                return RedirectToAction("Complete", new { slug = slug, hash = hash });
            }
            else
            {
                return RedirectToAction("securedfile", "errors");
            }
        }

        [HttpGet]
        public ActionResult Complete(string slug, string hash)
        {
            Slug fileSlug = _slugService.GetByName(slug);

            if (fileSlug.Deleted.HasValue)
                return RedirectToAction("deleted", "errors");

            if (fileSlug.Key.Equals(hash))
            {
                FileInfo file = null;

                if (fileSlug.FileID.HasValue)
                    file = _fileInfoService.GetBySlug(slug);

                List<Slug> slugs = _slugService.GetByParent(fileSlug.Name);

                var model = new FileViewModel()
                {
                    File = file,
                    Group = slugs,
                    Slug = fileSlug,
                    Account = _accountService.Current
                };

                if (file != null)
                {
                    model.FormattedSize = file.FormattedSize;
                }
                else if (slugs.Count > 0)
                {
                    long totalSize = 0;
                    slugs.ForEach(g => totalSize += g.File.Size);
                    model.FormattedSize = totalSize.FormatBytes();
                }

                return View(model);
            }
            else
            {
                return RedirectToAction("securedfile", "errors");
            }
        }

        /// <summary>
        /// Download the specified file
        /// </summary>
        /// <param name="slug">short name</param>
        /// <param name="hash">secure has for private downloads</param>
        /// <returns></returns>
        public ActionResult Download(string slug, string hash)
        {
            Slug fileSlug = _slugService.GetByName(slug);

            if (Request.UrlReferrer == null || (!Request.UrlReferrer.AbsolutePath.Contains(slug) && (fileSlug.Parent != null && fileSlug.Parent.Name.Equals(slug))))
                return RedirectToAction("Index", "Home", new { slug = slug });            

            if (fileSlug.Deleted.HasValue)
                return RedirectToAction("deleted", "errors");

            if (fileSlug.Type == Enums.SlugType.File)
            {
            }
            FileInfo file = _fileInfoService.GetBySlug(slug);

            if (file != null)
            {
                FileSend fileSend = null;
                bool isValidHash = (!string.IsNullOrEmpty(hash) && hash.Equals(fileSlug.Key));

                if (!string.IsNullOrEmpty(hash) && !hash.Equals(fileSlug.Key))
                {
                    dynamic details = HashUtils.DecodeDynamicHash(hash, fileSlug.Key);
                    isValidHash = true;

                    if (((string)details.Action).Equals("Email"))
                        fileSend = _fileSendService.GetByID((int)details.ID);
                }

                if (fileSlug.IsSecure && !isValidHash)
                    return RedirectToAction("SecuredFile", "Errors");

                long bytes = file.Size;
                string FormattedSize = bytes.FormatBytes();

                if (fileSend != null)
                {
                    // confirmation stuff here
                    _userMailer.Confirmation(fileSend).Send();
                }

                string path = Server.MapPath(Path.Combine("/storage", file.ID + file.Extension));
                string contentType = Utils.HttpUtils.GenerateContentType(file.Extension);

                return File(path, contentType, file.Filename);
            }
            else
                return RedirectToAction("NotFound", "Errors");
        }

        public ActionResult Delete(string slug, string hash, bool? confirm)
        {
            Slug fileSlug = _slugService.GetByName(slug);

            if (fileSlug.Deleted.HasValue)
                return RedirectToAction("Deleted", "Errors");

            if (fileSlug.Key.Equals(hash))
            {
                if (confirm.HasValue && confirm.Value)
                {
                    _slugService.Delete(fileSlug.ID);
                    return RedirectToAction("Index", "Home", new { slug = string.Empty, hash = string.Empty });
                }
                else
                {
                    FileInfo file = _fileInfoService.GetBySlug(slug);

                    var model = new FileViewModel()
                    {
                        File = file,
                        Slug = fileSlug
                    };

                    return View(model);
                }
            }
            else
            {
                return View();
            }
        }

        [HttpGet]
        public JsonResult ValidUrl(string url)
        {
            try
            {
                return Json(new { isValid = validateURL(url) }, JsonRequestBehavior.AllowGet);
            }
            catch (Exception ex)
            {
                return Json(new { error = ex.Message }, JsonRequestBehavior.AllowGet);
            }
        }

        private bool validateURL(string url)
        {
            try
            {
                //Creating the HttpWebRequest
                HttpWebRequest request = WebRequest.Create(url) as HttpWebRequest;
                //Setting the Request method HEAD, you can also use GET too.
                request.Method = "HEAD";
                //Getting the Web Response.
                HttpWebResponse response = request.GetResponse() as HttpWebResponse;
                //Returns TRUE if the Status code == 200
                return (response.StatusCode == HttpStatusCode.OK);
            }
            catch
            {
                //Any exception will returns false.
                return false;
            }
        }//validate url

        [HttpPost]
        public ActionResult UploadWeb(string url)
        {
            string ip = HttpContext.Request.UserHostAddress;
            int? accountId = _accountService.Current != null ? _accountService.Current.ID : default(int?);

            string uploadId = Guid.NewGuid().ToString();
            string folder = Server.MapPath("/storage/" + uploadId + "/");
            string basePath = Server.MapPath("/storage");
            bool valid = validateURL(url);

            Uri uri = new Uri(url);
            Directory.CreateDirectory(folder);
            string filePath = folder + Path.GetFileName(uri.LocalPath);

            // remove any previous uploads
            _fileInfoService.CleanUp(basePath);

            if (Request.IsAjaxRequest())
            {
                if (valid)
                {
                    _fileInfoService.Upload(ip, accountId, uploadId, folder, uri, filePath);

                    return Json(new { success = true, uploadId = uploadId, message = "Uploading link..." });
                }
                else
                {
                    return Json(new { success = false, message = "The link you entered was invalid", data = new { } });
                }
            }
            else
            {
                // need to implement the upload without the need for javascript
                return View();
            }
        }        

        void OnDownloadFileCompleted(object sender, System.ComponentModel.AsyncCompletedEventArgs e)
        {

        }

        [HttpPost]
        public JsonResult UploadWebProgress(string uploadId)
        {
            try
            {
                int progress = 0;
                long recieved = 0;
                long total = 0;
                string slug = string.Empty;
                string hash = string.Empty;
                string filePath = Server.MapPath(Path.Combine("/storage", uploadId));

                UploadProgress uploadProgress = _fileInfoService.GetUploadProgress(filePath, uploadId);

                progress = uploadProgress.Progress;
                recieved = uploadProgress.Recieved;
                total = uploadProgress.Total;
                slug = uploadProgress.Slug;
                hash = uploadProgress.Hash;

                return Json(new { success = true, progress = progress, recieved = recieved, total = total, slug = slug, hash = hash });
            }
            catch (Exception)
            {
                return Json(new { success = false });
            }
        }

        private void DeleteUploadProgress(string uploadId)
        {
            if (System.IO.File.Exists(Server.MapPath("/storage/" + uploadId)))
                System.IO.File.Delete(Server.MapPath("/storage/" + uploadId));
        }

        void OnDownloadWebProgressChanged(object sender, DownloadProgressChangedEventArgs e)
        {

        }

        private void SaveUploadProgress(UploadProgress uploadProgress)
        {

        }

        public JsonResult ShareDownload(string slug)
        {
            FileInfo fileInfo = _fileInfoService.GetBySlug(slug);

            if (fileInfo != null && _accountService.Current != null)
            {
                string token = _accountService.Current.FacebookToken;
                var client = new FacebookClient(token);
                dynamic post = client.Post("me/feed", new { message = string.Format("Download {0} from Rocca. Store", fileInfo.Filename), link = string.Format("http://{0}/{1}", Request.Url.Authority, slug), picture = string.Format("http://{0}/content/images/logo.png", Request.Url.Authority), name = "Rocca. Store" });

                return Json(new { success = true });
            }

            return Json(new { success = false });
        }
    }
}